#include <math.h>
#include <map>
#include "StandardSectionTool.h"

using SG::DataStructure::Common::Real;

using namespace std;
using SG::Algebra::Point;
using SG::FEMSolver::Element::Point2D;
using SG::FEMSolver::Element::StandardSectionTool;

void StandardSectionTool::MeshRect
(
const Point&  point,
const Real  a,
const Real  b,
const int     Na,
const int     Nb,
const int     nodeId0,
const int     eleId0,
_OUT Meshes&  meshes,
_OUT NodeSet& edgeNodes
)
{
    // 检查参数
    assert (b > 0 && Na > 0 && Nb > 0);

    meshes.clear ();
    edgeNodes.clear ();

    // 生成节点
    int nodeId = nodeId0 + 1;
    Real y, z;
    for (int i = 0; i < 2 * Na + 1; ++i)
    {
        y = point.m_y + a / Na / 2 * i;
        for (int j = 0; j < 2 * Nb + 1; ++j)
        {
            z = point.m_z + b / Nb / 2 * j;
            meshes.nodes.push_back (Point2D{nodeId, y, z});
            // 发现边界节点，将其保存到数组 `edgeNodes`
            if (fabs (point.m_y - y) < 1e-6 || fabs (point.m_y + a - y) < 1e-6 || fabs (point.m_z - z) < 1e-6 || fabs (point.m_z + b - z) < 1e-6)
                edgeNodes.push_back (Point2D{nodeId, y, z});
            nodeId = nodeId + 1;
        }
    }

    // 生成单元
    for (int i = 0; i < Na; ++i)
    {
        for (int j = 0; j < Nb; ++j)
        {
            int eleId = i * Nb + j + eleId0 + 1;
            int fristNodeId = i * (4 * Nb + 2) + j * 2 + nodeId0 + 1;
            if (a > 0)
            {
                meshes.q9Elements.push_back (Element{eleId, {
                    fristNodeId, fristNodeId + 4 * Nb + 2, fristNodeId + 4 * Nb + 4, fristNodeId + 2,              // Corner
                    fristNodeId + 2 * Nb + 1, fristNodeId + 4 * Nb + 3, fristNodeId + 2 * Nb + 3, fristNodeId + 1, // Mid
                    fristNodeId + 2 * Nb + 2}});                                                                   // Center
            }
            else
            {
                meshes.q9Elements.push_back (Element{eleId, {
                    fristNodeId + 4 * Nb + 2, fristNodeId, fristNodeId + 2, fristNodeId + 4 * Nb + 4,              // Corner
                    fristNodeId + 2 * Nb + 1, fristNodeId + 1, fristNodeId + 2 * Nb + 3, fristNodeId + 4 * Nb + 3, // Mid
                    fristNodeId + 2 * Nb + 2}});                                                                   // Center
            }
        }
    }
}

void StandardSectionTool::MeshRect2
(
const Point&  point,
const Real  a,
const Real  b,
const int     Na,
const int     Nb,
const int     nodeId0,
const int     eleId0,
_OUT Meshes&  meshes,
_OUT NodeSet& edgeNodes
)
{
    // 检查参数
    assert (a > 0 && Na > 0 && Nb > 0);

    meshes.clear ();
    edgeNodes.clear ();

    // 生成节点
    int nodeId = nodeId0 + 1;
    Real y, z;
    for (int j = 0; j < 2 * Nb + 1; ++j)
    {
        z = point.m_z + b / Nb / 2 * j;
        for (int i = 0; i < 2 * Na + 1; ++i)
        {
            y = point.m_y + a / Na / 2 * i;
            meshes.nodes.push_back (Point2D{nodeId, y, z});
            // 发现边界节点，将其保存到数组 `edgeNodes`
            if (fabs (point.m_y - y) < 1e-6 || fabs (point.m_y + a - y) < 1e-6 || fabs (point.m_z - z) < 1e-6 || fabs (point.m_z + b - z) < 1e-6)
                edgeNodes.push_back (Point2D{nodeId, y, z});
            nodeId = nodeId + 1;
        }
    }

    // 生成单元
    for (int j = 0; j < Nb; ++j)
    {
        for (int i = 0; i < Na; ++i)
        {
            int eleId = j * Na + i + eleId0 + 1;
            int fristNodeId = j * (4 * Na + 2) + i * 2 + nodeId0 + 1;
            if (b > 0)
            {
                meshes.q9Elements.push_back (Element{eleId, {
                    fristNodeId, fristNodeId + 2, fristNodeId + 4 * Na + 4, fristNodeId + 4 * Na + 2,              // Corner
                    fristNodeId + 1, fristNodeId + 2 * Na + 3, fristNodeId + 4 * Na + 3, fristNodeId + 2 * Na + 1, // Mid
                    fristNodeId + 2 * Na + 2}});                                                                   // Center
            }
            else
            {
                meshes.q9Elements.push_back (Element{eleId, {
                    fristNodeId + 4 * Na + 2, fristNodeId + 4 * Na + 4, fristNodeId + 2, fristNodeId,              // Corner
                    fristNodeId + 4 * Na + 3, fristNodeId + 2 * Na + 3, fristNodeId + 1, fristNodeId + 2 * Na + 1, // Mid
                    fristNodeId + 2 * Na + 2}});                                                                   // Center
            }
        }
    }
}

// 判断两个节点是否重合
bool isCoincident(const Point2D& p1, const Point2D& p2)
{
    // 节点重合，要排除节点相同的情况
    if ( p1.nodeId != p2.nodeId && fabs (p1.y - p2.y) < 1e-6 && fabs (p1.z - p2.z) < 1e-6)
        return true;
    else
        return false;
}

void StandardSectionTool::Merge
(
const NodeSet& edgeNodes,
const int      numLastRepeatNodes,
const Meshes&  newMeshes,
_OUT Meshes&   meshes,
_OUT int&      nodeId,
_OUT int&      eleId,
const int      numFrontRepeatNodes
)
{
    std::map<int, int> repeatNodes;
    const int nNewNodes = newMeshes.nodes.size ();
    // 遍历 `newMeshes` 中的后 numLastRepeatNodes 个节点
    for (int j = 0; j < numLastRepeatNodes; ++j)
    {
        int idRepeatNode = newMeshes.nodes[nNewNodes - j - 1].nodeId;
        // 找到该节点对应的重合节点的编号（重合节点只可能存在于边界 edgeNodes 中），并以键值对保存
        for (int iNode = edgeNodes.size () - 1; iNode >= 0; --iNode)
        {
            if (isCoincident (edgeNodes[iNode], newMeshes.nodes[nNewNodes - j - 1]))
            {
                repeatNodes[idRepeatNode] = edgeNodes[iNode].nodeId;
                break;
            }
        }
    }

    // 将新生成网格块中单元节点编号使用重合节点编号替换后，加入整体截面网格中
    for (auto element : newMeshes.q9Elements)
    {
        for (auto& node : element.nodes)
        {
            auto it = repeatNodes.find (node);
            if ( it == repeatNodes.end ())
                continue;

            // 当前节点位于重合边界上，需要将其替换成 `meshs` 已经存在的节点编号
            node = it->second;
        }
        
        meshes.q9Elements.push_back (element);
    }

    // 将新生成网格块的非重合节点添加到整体网格节点列表
    for (std::size_t i = numFrontRepeatNodes; i < newMeshes.nodes.size () - numLastRepeatNodes; ++i)
    {
        meshes.nodes.push_back (newMeshes.nodes[i]);
    }

    // 更新节点、单元最大Id，用于确定下一次网格生成第一个节点、单元的编号
    nodeId += newMeshes.nodes.size () - numFrontRepeatNodes - numLastRepeatNodes;
    eleId += newMeshes.q9Elements.size ();
}

void StandardSectionTool::Merge2 (const NodeSet& edgeNodes, const int numFrontRepeatNodes, _OUT Meshes& newMeshes)
{
    std::map<int, int> repeatNodes;
    // 遍历 `newMeshes` 中的前 numFrontRepeatNodes 个节点
    for (int j = 0; j < numFrontRepeatNodes; ++j)
    {
        int idRepeatNode = newMeshes.nodes[j].nodeId;
        // 找到该节点对应的重合节点的编号（重合节点只可能存在于边界 edgeNodes 中），并以键值对保存
        for (int iNode = edgeNodes.size () - 1; iNode >= 0; --iNode)
        {
            if (isCoincident (edgeNodes[iNode], newMeshes.nodes[j]))
            {
                repeatNodes[idRepeatNode] = edgeNodes[iNode].nodeId;
                // 更新新生成网格块节点的编号（重合节点，替换成相应重合节点的编号）
                newMeshes.nodes[j].nodeId = repeatNodes[idRepeatNode];
                break;
            }
        }
    }

    // 更新新生成网格块节点的编号（非重合节点，编号自减重合节点数，保证了节点编号的连续）
    for (std::size_t iNode = numFrontRepeatNodes; iNode < newMeshes.nodes.size (); ++iNode)
    {
        newMeshes.nodes[iNode].nodeId -= numFrontRepeatNodes;
    }

    // 跟随节点编号的更新，同步更新新生成网格块单元的节点编号
    for (auto& element : newMeshes.q9Elements)
    {
        for (int iNode = 0; iNode < 9; ++iNode)
        {
            auto it = repeatNodes.find (element.nodes[iNode]);
            if ( it == repeatNodes.end ())
            {
                // 非重合节点，编号自减重合节点数
                element.nodes[iNode] -= numFrontRepeatNodes;
            }
            else
            {
                // 重合节点，替换成相应重合节点的编号
                element.nodes[iNode] = it->second;
            }
        }
    }
}

void StandardSectionTool::UpdateEdgeNodes (const NodeSet& newEdgeNodes, const int numLastRepeatNodes, _OUT NodeSet& edgeNodes)
{
    for (std::size_t i = 0; i < newEdgeNodes.size () - numLastRepeatNodes; ++i)
    {
        edgeNodes.push_back (newEdgeNodes[i]);
    }
}